/**
    支持高级功能的模板类，如：
        使用模板和子模板自动填充数组
        用基本比较运算符进行条件处理
        基本数学函数支持
        用内置的模板变量执行任意内联代码
        自定义成员函数
        许多特殊的标记和内置运算符不被定义为API的一部分，但在模板中可以被支持

 **/


Ext.XTemplate = Ext.extend(Ext.Template, {
    argsRe: /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
    nameRe: /^<tpl\b[^>]*?for="(.*?)"/,
    ifRe: /^<tpl\b[^>]*?if="(.*?)"/,
    execRe: /^<tpl\b[^>]*?exec="(.*?)"/,
    constructor: function() {
        Ext.XTemplate.superclass.constructor.apply(this, arguments);

        var me = this,
            html = me.html,
            argsRe = me.argsRe,
            nameRe = me.nameRe,
            ifRe = me.ifRe,
            execRe = me.execRe,
            id = 0,
            tpls = [],
            VALUES = 'values',
            PARENT = 'parent',
            XINDEX = 'xindex',
            XCOUNT = 'xcount',
            RETURN = 'return ',
            WITHVALUES = 'with(values){ ',
            m, matchName, matchIf, matchExec, exp, fn, exec, name, i;

        // 将html以模版标签封装
        html = ['<tpl>', html, '</tpl>'].join('');

        // 匹配第一个<tpl>非tpl</tpl>，去最小匹配范围
        while ((m = html.match(argsRe))) {
            exp = null;
            fn = null;
            exec = null;
            matchName = m[0].match(nameRe); //满足匹配的正则表达式再去匹配for语句（捕获的是for定义的名称）
            matchIf = m[0].match(ifRe);//满足匹配的正则表达式再去匹配if语句（捕获的是if定义的名称）
            matchExec = m[0].match(execRe);//满足匹配的正则表达式再去匹配exec语句（捕获的是exec定义的名称）


            // 对if的捕获
            exp = matchIf ? matchIf[1] : null;

            if (exp) {
                fn = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + 'try{' + RETURN + Ext.util.Format.htmlDecode(exp) + ';}catch(e){return;}}');
            }

            /*
                function(values, parent, xindex, xcount){
                    with(values){ try{ return dfdsf; }catch(e){return;} }
                }
            */

            // 对exec的捕获
            exp = matchExec ? matchExec[1] : null;
            if (exp) {
                exec = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + Ext.util.Format.htmlDecode(exp) + ';}'); // decode将html转义字符转换成本义字符
            }

            /*
             function(values, parent, xindex, xcount){
                with(values){  }
             }
             */
            
            // 对for的捕获
            name = matchName ? matchName[1] : null;
            if (name) {
                if (name === '.') { // 如果是'.'，用'values'替代 ""
                    name = VALUES;
                } else if (name === '..') { // 如果是'..'，用'parent'替代
                    name = PARENT;
                }
                name = new Function(VALUES, PARENT, 'try{' + WITHVALUES + RETURN + name + ';}}catch(e){return;}');

                /*
                    function(values, parent) {
                        try{ with(values){ return values或parent; }}catch(e){return;}
                    }
                */
            }

            // 将信息传人tpls
            tpls.push({
                id: id,
                target: name,// for方法体
                exec: exec,// exec方法体
                test: fn,// if方法体
                body: m[1] || '' // tpl标签内的内容
            });

            // 把匹配到的tpl标签连同内容替换成{xtpl+id}
            html = html.replace(m[0], '{xtpl' + id + '}');
            id = id + 1;
        }

        // 从后往前遍历tpls数组
        for (i = tpls.length - 1; i >= 0; --i) {
            me.compileTpl(tpls[i]); // 编译每个子模版
        }

        // me.master 指向最外层模版
        me.master = tpls[tpls.length - 1];
        me.tpls = tpls;
    },

    // @private
    applySubTemplate: function(id, values, parent, xindex, xcount) {
        var me = this, t = me.tpls[id];
        return t.compiled.call(me, values, parent, xindex, xcount);
    },
    /**
     * @cfg {RegExp} codeRe The regular expression used to match code variables (default: matches <tt>{[expression]}</tt>).
     */
    codeRe: /\{\[((?:\\\]|.|\n)*?)\]\}/g,

    re: /\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\/]\s?[\d\.\+\-\*\/\(\)]+)?\}/g,


    /*

    tpls.push({
        id: id,
        target: name,// for方法体
        exec: exec,// exec方法体
        test: fn,// if方法体
        body: m[1] || '' // tpl标签内的内容
    });

    */
    // @private
    // 将插值转换成字符串方法
    compileTpl: function(tpl) {
        var fm = Ext.util.Format,
            me = this,
            useFormat = me.disableFormats !== true,
            body, bodyReturn, evaluatedFn;

        // m：匹配的全字符串，如"{aaa:bbb.ccc(dd) + 50}"
        // name: 属性名，如"aaa"
        // format: 运行的方法，如"bbb.ccc"
        // args: 方法的参数，如"dd"
        // math: 后续表达式，如" + 50"
        function fn(m, name, format, args, math) {
            var v;
            // name is what is inside the {}
            // Name begins with xtpl, use a Sub Template
            // 如果是子模版
            if (name.substr(0, 4) == 'xtpl') {
                // 将子模版转换成参数为模版id的方法
                return "',this.applySubTemplate(" + name.substr(4) + ", values, parent, xindex, xcount),'";
            }

            //  如果打括号内是"."
            // name = "." - Just use the values object.
            if (name == '.') {
                v = 'typeof values == "string" ? values : ""';
            }

            // name = "#" - Use the xindex
            else if (name == '#') { // 用索引
                v = 'xindex';
            }
            else if (name.substr(0, 7) == "parent.") { // 上级数据
                v = name;
            }
            // name has a . in it - Use object literal notation, starting from values
            else if (name.indexOf('.') != -1) { // 带下级属性
                v = "values." + name;
            }
            // 最普通的值
            // name is a property of values
            else {
                v = "values['" + name + "']";
            }

            // 如果后面有数学表达式
            if (math) {
                v = '(' + v + math + ')';
            }

            // 如果属性后面有方法
            if (format && useFormat) {
                args = args ? ',' + args : "";

                // 如果是普通方法
                if (format.substr(0, 5) != "this.") {
                    format = "fm." + format + '(';
                }
                else {
                    format = 'this.' + format.substr(5) + '(';
                }
            }
            else {
                args = '';
                format = "(" + v + " === undefined ? '' : ";
            }
            return "'," + format + v + args + "),'";
        }

        // m为大括号及大括号内的内容
        // code为中括号内的内容
        function codeFn(m, code) {
            // Single quotes get escaped when the template is compiled, however we want to undo this when running code.
            // 当编译模板时，单引号会被释放，但是我们希望在运行代码时撤消。
            return "',(" + code.replace(me.compileARe, "'") + "),'";
        }
        
        // 转义换行符、转义斜杠、
        bodyReturn = tpl.body.replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn).replace(me.codeRe, codeFn);

        // 模版编译后的生成函数字符串
        body = "evaluatedFmn = function(values, parent, xindex, xcount){return ['" + bodyReturn + "'].join('');};";

        // 创建编译后的函数
        eval(body);
                     
        // 每个模版的compiled方法   
        tpl.compiled = function(values, parent, xindex, xcount) {
            var vs, 
                length,
                buffer,
                i;
                
            // 如果if方法体且方法返回假值，直接跳出
            if (tpl.test && !tpl.test.call(me, values, parent, xindex, xcount)) {
                return '';
            } 
                           
            // 如果for方法体，运行方法体，以其返回值作为values
            vs = tpl.target ? tpl.target.call(me, values, parent) : values;
            if (!vs) {
               return '';
            }
   
            // 上级数据
            parent = tpl.target ? values : parent;

            // 有for方法体且返回值是数组
            if (tpl.target && Ext.isArray(vs)) {
                buffer = [], length = vs.length;

                // 如果有exec方法体
                if (tpl.exec) {

                    // 遍历for循环
                    for (i = 0; i < length; i++) {

                        //将每个子模版运行结果放入buffer数组中
                        buffer[buffer.length] = evaluatedFn.call(me, vs[i], parent, i + 1, length);

                        // 运行exec方法体
                        tpl.exec.call(me, vs[i], parent, i + 1, length);
                    }
                } else {

                    // 遍历for循环，将每个子模版运行结果放入buffer数组中
                    for (i = 0; i < length; i++) {
                        buffer[buffer.length] = evaluatedFn.call(me, vs[i], parent, i + 1, length);
                    }
                }
                return buffer.join(''); // 拼接字符串
            }
                
            // 如果有方法体
            if (tpl.exec) {
                tpl.exec.call(me, vs, parent, xindex, xcount);
            }

            // 模版运行结果
            return evaluatedFn.call(me, vs, parent, xindex, xcount);
        }
                
        return this;
    },

    /**
     * Returns an HTML fragment of this template with the specified values applied.
     * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
     * @return {String} The HTML fragment
     */
    applyTemplate: function(values) {
        return this.master.compiled.call(this, values, {}, 1, 1); // 编译最外层模版
    },

    /**
     * Compile the template to a function for optimized performance.  Recommended if the template will be used frequently.
     * @return {Function} The compiled function
     */
    compile: function() {
        return this;
    }
});
/**
 * Alias for {@link #applyTemplate}
 * Returns an HTML fragment of this template with the specified values applied.
 * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
 * @return {String} The HTML fragment
 * @member Ext.XTemplate
 * @method apply
 */
Ext.XTemplate.prototype.apply = Ext.XTemplate.prototype.applyTemplate;

/**
 * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
 * @param {String/HTMLElement} el A DOM element or its id
 * @return {Ext.Template} The created template
 * @static
 */
Ext.XTemplate.from = function(el, config) {
    el = Ext.getDom(el);
    return new Ext.XTemplate(el.value || el.innerHTML, config || {});
};